﻿// -----------------------------------------------------------------------
// <copyright file="BaseTypeSerializers.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentSerializer.Serializers
{
    using System;
    using System.Linq.Expressions;
    using System.Reflection;
    using CollectionsHelpers;
    using FluentSerializer.Utilities;

    /// <summary>
    /// 
    /// </summary>
    public static class BaseTypeSerializers
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sizeOf"></param>
        /// <returns></returns>
        public static Descriptor BuildBaseTypeSerializer(Type type, int sizeOf)
        {
            ParameterExpression item = Expression.Parameter(type);

            MethodInfo readMethod = type != typeof(byte) ? typeof(BinaryReaderStream).GetMethod("Read" + type.Name, BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null) : typeof(BinaryReaderStream).GetMethod("ReadFixedByte");
            MethodInfo writeMethod = typeof(BinaryWriterStream).GetMethod("Write", BindingFlags.Instance | BindingFlags.Public, null, new[] { type }, null);

            LambdaExpression deserializeExpression = Expression.Lambda(Expression.Call(Serializer.ReaderExpression, readMethod), Serializer.ReaderExpression, item);
            LambdaExpression serializeExpression = Expression.Lambda(Expression.Call(Serializer.WriterExpression, writeMethod, item), Serializer.WriterExpression, item);
            LambdaExpression sizeExpression = Expression.Lambda(Expression.Constant(sizeOf), item);

            var descriptor = new Descriptor(type, true, deserializeExpression, DeserializationHandling.RequireAssignment, serializeExpression, sizeExpression);
            return descriptor;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sizeOf"></param>
        /// <returns></returns>
        public static Descriptor BuildBaseTypeFixedLengthArraySerializer(Type type, int sizeOf)
        {
            ParameterExpression item = Expression.Parameter(type);
            Expression length = Expression.ArrayLength(item);

            Type[] pars = new[] { type, typeof(int), typeof(int) };

            MethodInfo readMethod = typeof(BinaryReaderStream).GetMethod("Read", BindingFlags.Instance | BindingFlags.Public, null, pars, null);
            MethodInfo writeMethod = typeof(BinaryWriterStream).GetMethod("Write", BindingFlags.Instance | BindingFlags.Public, null, pars, null);

            LambdaExpression deserializeExpression = Expression.Lambda(Expression.Block(Expression.Call(Serializer.ReaderExpression, readMethod, item, Serializer.Zero, length), item), Serializer.ReaderExpression, item);
            LambdaExpression serializeExpression = Expression.Lambda(Expression.Call(Serializer.WriterExpression, writeMethod, item, Serializer.Zero, length), Serializer.WriterExpression, item);
            LambdaExpression sizeExpression = Expression.Lambda(Expression.Multiply(Expression.Constant(sizeOf), length), item);

            type = typeof(FixedLength<>).MakeGenericType(type);

            var descriptor = new Descriptor(type, true, deserializeExpression, DeserializationHandling.None, serializeExpression, sizeExpression);
            return descriptor;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sizeOf"></param>
        /// <returns></returns>
        public static Descriptor BuildBaseTypeFixedLengthDirectListSerializer(Type type, int sizeOf)
        {
            Type arrayType = type.GetGenericArguments()[0].MakeArrayType();

            ParameterExpression item = Expression.Parameter(type);

            PropertyInfo count = type.GetProperty("Count", BindingFlags.Instance | BindingFlags.Public, null, typeof(int), Type.EmptyTypes, null);
            PropertyInfo items = type.GetProperty("Items", BindingFlags.Instance | BindingFlags.Public, null, arrayType, Type.EmptyTypes, null);

            MemberExpression count2 = Expression.Property(item, count);
            MemberExpression items2 = Expression.Property(item, items);

            Type[] pars = new[] { arrayType, typeof(int), typeof(int) };

            MethodInfo readMethod = typeof(BinaryReaderStream).GetMethod("Read", BindingFlags.Instance | BindingFlags.Public, null, pars, null);
            MethodInfo writeMethod = typeof(BinaryWriterStream).GetMethod("Write", BindingFlags.Instance | BindingFlags.Public, null, pars, null);

            LambdaExpression deserializeExpression = Expression.Lambda(Expression.Block(Expression.Call(Serializer.ReaderExpression, readMethod, items2, Serializer.Zero, count2), item), Serializer.ReaderExpression, item);
            LambdaExpression serializeExpression = Expression.Lambda(Expression.Call(Serializer.WriterExpression, writeMethod, items2, Serializer.Zero, count2), Serializer.WriterExpression, item);
            LambdaExpression sizeExpression = Expression.Lambda(Expression.Multiply(Expression.Constant(sizeOf), count2), item);

            type = typeof(FixedLength<>).MakeGenericType(type);

            var descriptor = new Descriptor(type, true, deserializeExpression, DeserializationHandling.None, serializeExpression, sizeExpression);
            return descriptor;
        }
    }
}